﻿// SyntheticDataGenerator.Program
//
// (c) 2011 Arthur Pitman
//
// Part of Market-Basket Synthetic Data Generator
// A C# adaptation of the IBM Quest Market-Basket Synthetic Data Generator
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// (Version 2) as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License (Version 2) along with this program; if not, write to 
// the Free Software Foundation, Inc., 
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// or see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

using SyntheticDataGenerator.ParameterSets;
using System.Reflection;

namespace SyntheticDataGenerator
{
	/// <summary>
	/// Main class of SyntheticDataGenerator application
	/// </summary>
	public class Program
	{     
		/// <summary>
		/// The main method of the program
		/// </summary>
		/// <param name="args">Command line arguments</param>
		static void Main(string[] args)
		{
			var version = Assembly.GetExecutingAssembly().GetName().Version;
			Console.WriteLine("SyntheticDataGenerator v{0}", version);
			Console.WriteLine();

			// get command
			string command = "";
			if (args.Length > 0)
				command = args[0].ToLower();

			// choose which function to perform
			switch (command)
			{
				case "lit":
					var transactionParameters = new TransactionParameters();
					if (GetParametersFromArgs(args, transactionParameters))
						new TransactionCreator(transactionParameters);
					else
						DisplayTransactionCreatorUsage();
					break;

				case "tax":
					var taxonomyParameters = new TaxonomyParameters();
					if (GetParametersFromArgs(args, taxonomyParameters))
					{
						taxonomyParameters.CalculateValues();
						new TaxonomyCreator(taxonomyParameters);
					}
					else
					{
						DisplayTaxonomyCreatorUsage();
					}
					break;

				case "seq":
					var sequenceParameters = new SequenceParameters();
					if (GetParametersFromArgs(args, sequenceParameters))
						new SequenceCreator(sequenceParameters);
					else
						DisplaySequenceCreatorUsage();
					break;

				default:					
					Console.WriteLine("(c) Copyright 2011 Arthur Pitman");
					Console.WriteLine("This program is made available under GNU General Public License (Version 2)");
					Console.WriteLine("An open-source C# market-basket synthetic data generator based on the IBM Quest Market-Basket Synthetic Data Generator.");
					Console.WriteLine();
					Console.WriteLine("Usage: {0} lit | tax | seq [options]", Process.GetCurrentProcess().ProcessName);
					break;
			}
		}

		/// <summary>
		/// Displays general usage information
		/// </summary>
		protected static void DisplayGeneralCreatorUsage(CreatorParameters parameters)
		{
			Console.WriteLine("-fname [filename] : base file name");
			Console.WriteLine("-tlen [double] : average transaction length (default {0})", parameters.AverageTransactionLength);
			Console.WriteLine("-nitems [integer] : item count (default {0})", parameters.ItemCount);
			Console.WriteLine("-randseed [integer] : master random seed, must be <= 0 (default {0})", parameters.MasterSeed);
			Console.WriteLine("-lit.npats [integer] : large item set pattern count (default {0})", parameters.LisParameters.PatternCount);
			Console.WriteLine("-lit.patlen [double] : large item set average pattern length (default {0})", parameters.LisParameters.AverageLength);
			Console.WriteLine("-lit.corr [double] : large item set correlation (-corr) (default {0})", parameters.LisParameters.Correlation);
			Console.WriteLine("-lit.conf [double] : large item set confidence (-conf) (default {0})", parameters.LisParameters.Confidence);
		}

		/// <summary>
		/// Displays transaction creator usage information
		/// </summary>
		protected static void DisplayTransactionCreatorUsage()
		{			
			Console.WriteLine("Transaction creator usage: {0} lit [options]", Process.GetCurrentProcess().ProcessName);
			Console.WriteLine();
			Console.WriteLine("Options:");

			var transactionParameters = new TransactionParameters();
			DisplayGeneralCreatorUsage(transactionParameters);
			Console.WriteLine("-ntrans [integer] : transaction count (default {0})", transactionParameters.TransactionCount);
		}

		/// <summary>
		/// Displays taxonomy creator usage information
		/// </summary>
		protected static void DisplayTaxonomyCreatorUsage()
		{
			Console.WriteLine("Taxonomy creator usage: {0} tax [options]", Process.GetCurrentProcess().ProcessName);
			Console.WriteLine();
			Console.WriteLine("Options:");

			var taxonomyParameters = new TaxonomyParameters();
			DisplayGeneralCreatorUsage(taxonomyParameters);
			Console.WriteLine("-nroots [integer] : number of roots (default {0})", taxonomyParameters.RootCount);
			Console.WriteLine("-nlevels [double] : number of levels (default {0})", taxonomyParameters.AverageLevelCount);
			Console.WriteLine("-fanout [double] : fanout degree (default {0})", taxonomyParameters.Fanout);
			Console.WriteLine("-depth [double] : taxonomy depth ratio (default {0})", taxonomyParameters.DepthRatio);
		}

		/// <summary>
		/// Displays sequence creator usage information
		/// </summary>
		protected static void DisplaySequenceCreatorUsage()
		{
			Console.WriteLine("Sequence creator usage: {0} seq [options]", Process.GetCurrentProcess().ProcessName);
			Console.WriteLine();
			Console.WriteLine("Options:");

			var sequenceParameters = new SequenceParameters();
			DisplayGeneralCreatorUsage(sequenceParameters);
			Console.WriteLine("-ncust [integer] : number of customers / sequences (-nseq) (default {0})", sequenceParameters.SequenceCount);
			Console.WriteLine("-slen [double] : average sequence length (default {0})", sequenceParameters.AverageSequenceLength);
			Console.WriteLine("-seq.npats [integer] : sequence pattern count (default {0})", sequenceParameters.LSeqParameters.PatternCount);
			Console.WriteLine("-seq.patlen [double] : sequence average pattern length (default {0})", sequenceParameters.LSeqParameters.AverageLength);
			Console.WriteLine("-seq.corr [double] : sequence correlation (default {0})", sequenceParameters.LSeqParameters.Correlation);
			Console.WriteLine("-seq.conf [double] : sequence confidence (default {0})", sequenceParameters.LSeqParameters.Confidence);
			Console.WriteLine("-flat [boolean] : write sequences in flat format (default {0})", sequenceParameters.FlatSequences);
		}

		protected static void DisplayInvalidParameterError(string parameterName)
		{
			Console.WriteLine("Error in parameter \"{0}\"", parameterName);
		}

		/// <summary>
		/// Parses parameters from command line arguments
		/// </summary>
		/// <param name="args"></param>
		/// <param name="parameters"></param>
		protected static bool GetParametersFromArgs(string[] args, CreatorParameters parameters)
		{
			// loop through all arguments
			for (int argIdx = 1; argIdx < args.Length; argIdx++)
			{
				// all parameter names start with a "-"
				var parameterName = args[argIdx];
				if (parameterName.StartsWith("-"))
				{
					argIdx++;
					if (argIdx >= args.Length)
					{
						DisplayInvalidParameterError(parameterName);
						break;
					}
					var parameterValue = args[argIdx];
					switch (parameterName)
					{
						// CreatorParameters
						case "-fname":
							parameters.BaseFilename = parameterValue;
							break;

						case "-tlen":
							parameters.AverageTransactionLength = double.Parse(parameterValue);
							break;

						case "-nitems":
							parameters.ItemCount = int.Parse(parameterValue);
							break;
					  
						case "-randseed":
							parameters.MasterSeed = int.Parse(parameterValue);
							if (parameters.MasterSeed > 0)
							{
								parameters.MasterSeed = 0;
								Console.WriteLine("Warning: randseed must be negative, or zero");
							}
							break;

						case "-lit.npats":
						case "-npats":
							parameters.LisParameters.PatternCount = int.Parse(parameterValue);
							break;

						case "-lit.patlen":
						case "-patlen":
							parameters.LisParameters.AverageLength = double.Parse(parameterValue);
							break;

						case "-lit.corr":
						case "-corr":
							parameters.LisParameters.Correlation = double.Parse(parameterValue);
							break;

						case "-lit.conf":
						case "-conf":
							parameters.LisParameters.Confidence = double.Parse(parameterValue);
							break;

						// TransactionParameters
						case "-ntrans":
							if (parameters is TransactionParameters)
								((TransactionParameters)parameters).TransactionCount = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);								
							break;

						// TaxonomyParameters
						case "-nroots":
							if (parameters is TaxonomyParameters)
								((TaxonomyParameters)parameters).RootCount = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-nlevels":
							if (parameters is TaxonomyParameters)
								((TaxonomyParameters)parameters).AverageLevelCount = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-fanout":
							if (parameters is TaxonomyParameters)
								((TaxonomyParameters)parameters).Fanout = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-depth":
							if (parameters is TaxonomyParameters)
								((TaxonomyParameters)parameters).DepthRatio = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;


						// SequenceParameters
						case "-ncust":
						case "-nseq":
							if (parameters is SequenceParameters)
								((SequenceParameters)parameters).SequenceCount = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-slen":
							if (parameters is SequenceParameters)
								((SequenceParameters)parameters).AverageSequenceLength = double.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-seq.npats":
							if (parameters is SequenceParameters)
								((SequenceParameters)parameters).LSeqParameters.PatternCount = int.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-seq.patlen":
							if (parameters is SequenceParameters)
								((SequenceParameters)parameters).LSeqParameters.AverageLength = double.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-seq.corr":
							if (parameters is SequenceParameters)
								((SequenceParameters)parameters).LSeqParameters.Correlation = double.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-seq.conf":
							if (parameters is SequenceParameters)
								((SequenceParameters)parameters).LSeqParameters.Confidence = double.Parse(parameterValue);
							else
								DisplayInvalidParameterError(parameterName);
							break;

						case "-flat":
							if (parameters is SequenceParameters)
							{
								bool flatSequences = false;
								bool.TryParse(parameterValue, out flatSequences);
								((SequenceParameters)parameters).FlatSequences = flatSequences;
							}
							else
								DisplayInvalidParameterError(parameterName);
							break;  

						default:
							DisplayInvalidParameterError(parameterName);
							break;
					}
				}
				else
				{					
					return false;
				}

			}

			// a filename must be specified for the parameters to be valid
			return (parameters.BaseFilename.Length > 0);
		}

	}
}
